package com.lhy.hierarchy.ruleEngine.composite.service.impl;

import com.hauxsoft.core.constant.Consts;
import com.hauxsoft.core.constant.Errors;
import com.hauxsoft.core.mvc.dao.BaseDao;
import com.hauxsoft.core.mvc.dao.NativeQuery;
import com.hauxsoft.core.mvc.dao.SqlParamValueParser;
import com.hauxsoft.core.mvc.service.impl.BaseFlatCrudServiceImpl;
import com.hauxsoft.core.util.BeanUtils2;
import com.hauxsoft.core.util.StringUtils2;
import com.hauxsoft.core.util.result.Result;
import com.hauxsoft.core.util.result.Results;
import com.hauxsoft.dao.JavaRuleDao;
import com.hauxsoft.data.CompileResult;
import com.hauxsoft.data.JavaRuleDTO;
import com.hauxsoft.entity.JavaRuleDO;
import com.hauxsoft.service.JavaRuleService;
import com.hauxsoft.utils.BaseRule;
import com.hauxsoft.utils.DynamicRuleUtils;
import com.hauxsoft.utils.JavaRuleStorage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Service
public class JavaRuleServiceImpl extends BaseFlatCrudServiceImpl<JavaRuleDO, Long> implements JavaRuleService{

    @Override
    protected BaseDao<JavaRuleDO, Long> dao() {
        return dao;
    }
    
    /**
     * return jpa entity by id
     * @param id
     * @return entity in JAVA_RULE
     */
    @Override
    public JavaRuleDO get(Long id) {
        return dao.findById(id).orElse(null);
    }

    /**
     * insert or update jpa entity
     * @param entity
     */
    @Override
    @Transactional
    public void save(JavaRuleDO entity) {
        Date now = new Date();
        boolean isNew = entity.getId() == null;
        if(isNew) {
            entity.setCreateTime(now);
        }
        else {
            entity.setUpdateTime(now);
        }
        dao.save(entity);
    }
    
    /**
     * delete jpa entity by id
     * @param id
     * @return Results.SUCCESS_RESULT if success, or ErrorResult instance if error
     */
    @Override
    @Transactional
    public Result<?> delete(Long id) {
        JavaRuleDO entity = get(id);
        return delete(entity);         
    }

    /**
     * delete jpa entity by id
     * @param id
     * @return Results.SUCCESS_RESULT if success, or ErrorResult instance if error
     */
    @Override
    @Transactional
    public Result<?> delete(JavaRuleDO entity) {
        return Results.when(entity)
            .map(e -> e == null, "entity is not exist", Errors.Entity.NOT_EXIST)
            .map(e -> e.getIsDeleted().equals(Consts.Entity.IS_DELETED), "entity is deleted", Errors.Entity.IS_DELETED)
            // 设定正在使用的规则不能删除，必须先停掉
            .map(e -> Objects.equals(e.getStatus(), Consts.Entity.IS_VALID), "规则处于启用状态，不能删除！")
            .run(e -> {
                e.setIsDeleted(Consts.Entity.IS_DELETED);
                dao.save(e);
            })
            // 从容器移除
    		/*.then(entity2 -> {
    			String groupName = entity2.getGroupName();
				try {
					BaseRule rule = RuleUtils.getRuleInstance(entity2);
					if (javaRuleStorage.contains(groupName, rule) && !javaRuleStorage.remove(groupName, rule)) {
						return Results.error("从容器移除规则失败！");
					}
				} catch (Exception e) {
					log.error("从容器移除规则{}异常！", entity2.getName(), e);
					return Results.error("从容器移除规则异常！");
				}
    			
    			return Results.success();
    		})*/;
    }

    /**
     * execute native sql query by conditions in query html page
     * @param conditions 查询条件
     * @pageNo 页码，从0开始
     * @pageSize 每页数据量，必须 > 0
     * @sort 排序方式
     * @return Results.SUCCESS_RESULT if success, or ErrorResult instance if error
     */
    @Override
    public Page<Map<String, Object>> query(Map<String, Object> conditions, int pageNo, int pageSize, String sort) throws ParseException {
        NativeQuery query = dao.createNativeQuery()
                .select("t.id, t.TARGET, t.GROUP_NAME, t.SIMPLE_CLASS_NAME, t.NAME, t.SORT, t.CREATE_TIME, t.STATUS")
                .from("JAVA_RULE t")
                .where("t.TARGET like :target") 
                .where("t.GROUP_NAME like :groupName") 
                .where("t.SIMPLE_CLASS_NAME like :simpleClassName") 
                .where("t.NAME like :name") 
                .where("t.STATUS = :status") 
                .where("t.IS_DELETED = " + Consts.Entity.NOT_DELETED) 
                .registParameterValueParser("target", SqlParamValueParser.LEFT_LIKE_PARSER)
                .registParameterValueParser("groupName", SqlParamValueParser.LEFT_LIKE_PARSER)
                .registParameterValueParser("simpleClassName", SqlParamValueParser.LEFT_LIKE_PARSER)
                .registParameterValueParser("name", SqlParamValueParser.CONTAIN_PARSER)
                .sort(sort)
                .setParameters(conditions);
        
        // 系统筛选
        if (StringUtils2.notEmpty(ruleTarget)) {
			query.where("target in :targets")
			.setParameter("targets", Arrays.asList(ruleTarget.split(",")));
		}
                        
        return query.listPage(pageNo, pageSize);
    }

    /**
     * 返回dto对象 
     * @param id
     * @return success result if entity exist, or error result if entity not exist
     */
    @Override
    public Result<JavaRuleDTO> getDTO(Long id) {
        return getOne(id).map(JavaRuleDTO::of);
    }
    
    /**
     * 根据dto对象insert一条数据，dto对象的内容一般来自新增页面
     * @param id
     * @return success or error result
     */
    @Override
    @Transactional
    public Result<?> insertDTO(JavaRuleDTO dto){
        return Results.when(dto.toEntity("id", "createTime", "updateTime"))
    			// 编译
        		.then(this::compiler)
        		// 保存
        		.run(this::save)
        		// 添加到bean容器或者移除
        		.then(entity -> {
        			// 如果是启动状态，顺便添加规则到容器
    				if (Objects.equals(entity.getStatus(), Consts.Entity.IS_VALID)) {
    					return addRuleToStorage(entity);
    				} else if (Objects.equals(entity.getStatus(), Consts.Entity.NOT_VALID)) {
    					String groupName = entity.getGroupName();
    					try {
    						BaseRule rule = DynamicRuleUtils.getRuleInstance(entity);
    						if (javaRuleStorage.contains(groupName, rule) && !javaRuleStorage.remove(groupName, rule)) {
    							return Results.error("从容器移除规则失败！");
    						}
    					} catch (Exception e) {
    						log.error("从容器移除规则{}异常！", entity.getName(), e);
    						return Results.error("从容器移除规则异常！");
    					}
    				}
    				return Results.success(entity);
        		})
                .map(e -> {
                    dto.setId(e.getId());
                    dto.setUpdateTime(e.getCreateTime());
                    return dto;
                });
    }
    
    /**
     * 根据dto对象update一条数据，dto对象的内容一般来自修改页面
     * @param id
     * @return success or error result
     */
    @Override
    @Transactional
    public Result<?> updateDTO(JavaRuleDTO dto){
        return getOne(dto.getId())
                .then(e -> {
                    BeanUtils2.copyProperties(dto.toEntity(), e, "id", "createTime", "updateTime", "createUserId", "createUserName");                    
                    return Results.success(e);
                })
                // 编译
        		.then(this::compiler)
                // 保存
                .run(this::save)
                // 添加到bean容器或者移除
        		.then(entity -> {
        			// 如果是启动状态，顺便添加规则到容器
    				if (Objects.equals(entity.getStatus(), Consts.Entity.IS_VALID)) {
    					return addRuleToStorage(entity);
    				} else if (Objects.equals(entity.getStatus(), Consts.Entity.NOT_VALID)) {
    					String groupName = entity.getGroupName();
    					try {
    						BaseRule rule = DynamicRuleUtils.getRuleInstance(entity);
    						if (javaRuleStorage.contains(groupName, rule) && !javaRuleStorage.remove(groupName, rule)) {
    							return Results.error("从容器移除规则失败！");
    						}
    					} catch (Exception e) {
    						log.error("从容器移除规则{}异常！", entity.getName(), e);
    						return Results.error("从容器移除规则异常！");
    					}
    				}
    				return Results.success(entity);
        		})
                .map(e -> {
                    dto.setUpdateTime(e.getCreateTime());
                    return dto;
                });
    }
    
    // 编译
    private Result<JavaRuleDO> compiler(JavaRuleDO entity) {
    	Result<?> result = DynamicRuleUtils.compile(entity.getSrcCode());
    	if (result.isError()) {
			return (Result<JavaRuleDO>) result;
		}
    	CompileResult compileResult = (CompileResult) result.get();
    	for (String classFullName : compileResult.getByteCode().keySet()) {
    		int lastIndex = classFullName.lastIndexOf(".");
			String simpleName = lastIndex != -1 ? classFullName.substring(lastIndex + 1) : classFullName,
				fileName = compileResult.getMainClassFileName();
			// 只要最外层的类
			if (fileName.startsWith(simpleName)) {
				entity.setFileName(fileName);
				entity.setFullClassName(classFullName);
				entity.setSimpleClassName(simpleName);
				entity.setByteContent(compileResult.getByteCode().get(classFullName));
				return Results.success(entity);
			}
		}
    	return Results.error("没有找到最外层类！");
    }
    
    /**
     * 添加规则到容器
     * @param entity
     * @return
     */
    private Result<JavaRuleDO> addRuleToStorage(JavaRuleDO entity) {
		try {
			BaseRule rule = DynamicRuleUtils.getRuleInstance(entity);
			return javaRuleStorage.add(entity.getGroupName(), rule) ? Results.success(entity)
	    			: Results.error("添加规则到容器失败！");
		} catch (Exception e) {
			log.error("添加规则{}到容器异常！", entity.getName(), e);
			return Results.error("添加规则到容器异常！");
		}
    }
    
    /**
     * 从容器移除规则
     * @param entity
     * @return
     */
    private Result<JavaRuleDO> removeRuleFromStorage(JavaRuleDO entity) {
		try {
			BaseRule rule = DynamicRuleUtils.getRuleInstance(entity);
			return javaRuleStorage.remove(entity.getGroupName(), rule) ? Results.success(entity)
	    			: Results.error("从容器移除规则失败！");
		} catch (Exception e) {
			log.error("从容器移除规则{}异常！", entity.getName(), e);
			return Results.error("从容器移除规则异常！");
		}
    	
    }
    
    /**
     * 返回entity对象，如果entity不存在，或者被逻辑删除，则返回error result对象
     * @param id
     * @return success or error result
     */
    @Override
    public Result<JavaRuleDO> getOne(Long id) {
        return Results.when(get(id))    
                .map(e -> e == null, "entity not exist", Errors.Entity.NOT_EXIST)
                .map(e -> e.getIsDeleted().equals(Consts.Entity.IS_DELETED), "entity is deleted", Errors.Entity.IS_DELETED);
    }
    
    /**
     * 返回有效状态的entity对象，如果entity不存在，或者被逻辑删除，则返回error result对象
     * @param id
     * @return success or error result
     */ 
    @Override
    public Result<JavaRuleDO> getValidOne(Long id) {
        return getOne(id)
                .map(e -> !e.getStatus().equals(Consts.Entity.IS_VALID), "entity is not valid", Errors.Entity.NOT_VALID);
    }
      
    /**
     * 切换entity.status数值，并保存
     * @param id entity.id
     * @param oldStatus entity当前应当处于的状态值，如果不匹配则返回错误提示
     * @param newStatus 需要切换的新状态值
     * @param errorPrompt 当entity.status != oldStatus时，返回的错误提示语
     * @param errorCode 当entity.status != oldStatus时，返回的错误编码
     * @return
     */
    private Result<JavaRuleDO> changeEntityStatus(JavaRuleDO entity, Integer oldStatus, Integer newStatus, 
            String errorPrompt, Integer errorCode) {
        return Results.when(entity)
                .map(e -> !e.getStatus().equals(oldStatus), errorPrompt, errorCode)
                .run(e -> {
                    e.setStatus(newStatus);
                    save(e);
                });
    }

    /**
     * enable, set entity.status from 0 -> 1
     * @param id
     * @return
     */
    @Override
    @Transactional
    public Result<JavaRuleDO> enable(Long id) {
        JavaRuleDO entity = get(id);
        return enable(entity);
    }
    
    /**
     * enable, set entity.status from 0 -> 1
     * @param id
     * @return
     */
    @Override
    @Transactional
    public Result<JavaRuleDO> enable(JavaRuleDO entity) {
        Integer oldStatus = Consts.Entity.BEFORE_APPROVAL, 
                newStatus =  Consts.Entity.IS_VALID;
        Result<JavaRuleDO> changeResult = changeEntityStatus(entity, oldStatus, newStatus, "entity do not allow enable operate", null);
        if (changeResult.isError()) {
			return changeResult;
		}
        // 添加规则到容器
        return addRuleToStorage(entity);
        
    }

    /**
     * disable, set entity.status from 1 -> 0
     * @param id
     * @return
     */
    @Override
    @Transactional
    public Result<JavaRuleDO> disable(JavaRuleDO entity) {
        Integer oldStatus = Consts.Entity.IS_VALID, 
                newStatus =  Consts.Entity.BEFORE_APPROVAL;
        Result<JavaRuleDO> changeResult = changeEntityStatus(entity, oldStatus, newStatus, "entity is not valid", null);
        if (changeResult.isError()) {
			return changeResult;
		}
        // 从容器把规则移除
        return removeRuleFromStorage(entity);
    }
    
    @Override
    @Transactional
    public Result<JavaRuleDO> disable(Long id) {
        JavaRuleDO entity = get(id);
        return disable(entity);
    }

    /**
     * submit, set entity.status from -1 -> 0
     * @param id
     * @return
     */
    @Override
    @Transactional
    public Result<JavaRuleDO> submit(JavaRuleDO entity) {
        Integer oldStatus = Consts.Entity.IS_DRAFT, 
                newStatus =  Consts.Entity.BEFORE_APPROVAL;
        return changeEntityStatus(entity, oldStatus, newStatus, "entity is not draft", null);
    }
    
    @Override
    @Transactional
    public Result<JavaRuleDO> submit(Long id) {
        JavaRuleDO entity = get(id);
        return submit(entity);
    }

    /**
     * withdraw, set entity.status from 0 -> -1
     * @param id
     * @return
     */
    @Override
    public Result<JavaRuleDO> withdraw(JavaRuleDO entity) {
        Integer oldStatus = Consts.Entity.BEFORE_APPROVAL, 
                newStatus =  Consts.Entity.IS_DRAFT;
        return changeEntityStatus(entity, oldStatus, newStatus, "entity do not allow widthdraw", null);
    }
    
    @Override
    @Transactional
    public Result<JavaRuleDO> withdraw(Long id) {
        JavaRuleDO entity = get(id);
        return withdraw(entity);
    }
    
    /**
     * recover，set entity.isDeleted from 1 -> 0
     * @param id
     * @return
     */
    @Override
    @Transactional
    public Result<JavaRuleDO> recover(Long id){
        JavaRuleDO entity = get(id);
        return recover(entity);
    }
    
    @Override
    @Transactional
    public Result<JavaRuleDO> recover(JavaRuleDO entity){
        return Results.when(entity)
                .map(e -> e == null, "entity not exist", Errors.Entity.NOT_EXIST)
                .map(e -> !e.getIsDeleted().equals(Consts.Entity.IS_DELETED), "entity is not deleted")
                .run(e -> {
                    e.setIsDeleted(Consts.Entity.NOT_DELETED);
                    save(e);
                });
    }
    
    @Autowired
    private JavaRuleDao dao;
    @Autowired
    private JavaRuleStorage javaRuleStorage;
    @Value("${dynamic.rule.target}")
	private String ruleTarget;
    
}